#ifndef RTPPARSE_UTILS_H
#define RTPPARSE_UTILS_H

#include <string>
#include <limits>

#include <arpa/inet.h>  // htonl(), htons(), ntohl(), ntohs()
#include <sys/socket.h>
#include <netinet/in.h>

namespace Utils {

    class IP {
    public:
        static int GetFamily(const std::string& ip);
        static void NormalizeIp(std::string& ip);
        static void GetAddressInfo(const struct sockaddr* addr, int& family, std::string& ip, uint16_t& port);
    };

    template<typename T, uint8_t N = 0>
    class Number {
    private:
        static constexpr T MaxValue = (N == 0) ? std::numeric_limits<T>::max() : ((1 << N) - 1);
        static constexpr T Mask =
		  (N == 0) ? std::numeric_limits<T>::max() : (static_cast<T>((T(1) << N) - 1));
    public:
        static bool IsLowerThan(T lhs, T rhs) {
            static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> || std::is_same_v<T, uint64_t>,
                          "T must be one of the following types: uint8_t, uint16_t, uint32_t, or uint64_t");

            lhs &= Mask;
            rhs &= Mask;

            return ((rhs > lhs) && (rhs - lhs <= MaxValue / 2)) ||
                   ((lhs > rhs) && (lhs - rhs > MaxValue / 2));
        }

        static bool IsHigherThan(T lhs, T rhs) {
            static_assert(std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> || std::is_same_v<T, uint64_t>,
                          "T must be one of the following types: uint8_t, uint16_t, uint32_t, or uint64_t");

            lhs &= Mask;
            rhs &= Mask;
            return ((lhs > rhs) && (lhs - rhs <= MaxValue / 2)) ||
                   ((rhs > lhs) && (rhs - lhs > MaxValue / 2));
        }

        static bool IsHigherOrEqualThan(T lhs, T rhs) {
			static_assert(
			  std::is_same_v<T, uint8_t> || std::is_same_v<T, uint16_t> || std::is_same_v<T, uint32_t> ||
			    std::is_same_v<T, uint64_t>,
			  "T must be uint8_t, uint16_t, uint32_t or uint64_t");

			lhs &= Mask;
			rhs &= Mask;

			return (lhs == rhs) || ((lhs > rhs) && (lhs - rhs <= MaxValue / 2)) ||
			       ((rhs > lhs) && (rhs - lhs > MaxValue / 2));
		}
    };

    class Byte {
    public:
      static uint8_t Get1Byte(const uint8_t* data, size_t i) {
        return data[i];
      }

      static void Set1Byte(uint8_t *data, size_t i, uint8_t value) {
        data[i] = value;
      }

      static uint16_t Get2Bytes(const uint8_t* data, size_t i) {
        return uint16_t{ data[i + 1]} | uint16_t{ data[i] } << 8;
      }

      static uint32_t Get3Bytes(const uint8_t* data, size_t i) {
        return uint32_t{ data[i + 2]} | uint32_t{ data[i + 1] } << 8 | uint32_t{ data[i] } << 16;
      }

      static void Set3Bytes(uint8_t *data, size_t i, uint32_t value) {
        data[i + 2] = static_cast<uint8_t>(value);
        data[i + 1] = static_cast<uint8_t>(value >> 8);
        data[i] = static_cast<uint8_t>(value >> 16); 
      }

      static uint32_t Get4Bytes(const uint8_t *data, size_t i) {
        return uint32_t{ data[i + 3]} | uint32_t{ data[i + 2]} << 8 | uint32_t {data[i + 1]} << 16 | uint32_t{data[i]} << 24;  
      }

      static void Set4Bytes(uint8_t* data, size_t i, uint32_t value) {
			  data[i + 3] = static_cast<uint8_t>(value);
			  data[i + 2] = static_cast<uint8_t>(value >> 8);
			  data[i + 1] = static_cast<uint8_t>(value >> 16);
			  data[i]     = static_cast<uint8_t>(value >> 24);
		  }

      static void Set2Bytes(uint8_t* data, size_t i, uint16_t value) {
			  data[i + 1] = static_cast<uint8_t>(value);
			  data[i]     = static_cast<uint8_t>(value >> 8);
		  }
    };

    class Bits {
    public:
      static size_t CountSetBits(const uint16_t mask) {
        // return static_cast<size_t>(__Builtin_popcount(mask));
        return static_cast<size_t>(mask);
      }
    };

}

#endif // RTPPARSE_UTILS_H